package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.heima.api.item.ItemClient;
import com.heima.common.PageResult;
import com.heima.common.Result;
import com.heima.constant.ESConstant;
import com.heima.constant.ItemConstant;
import com.heima.dto.SearchDTO;
import com.heima.po.ItemDoc;
import com.heima.search.service.SearchService;

import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.configurationprocessor.json.JSONArray;
import org.springframework.boot.configurationprocessor.json.JSONObject;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.*;


@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    RestHighLevelClient restHighLevelClient;
    @Autowired
    ItemClient itemClient;

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public Result createIndexAndMapping() throws IOException {
        CreateIndexRequest indexRequest = new CreateIndexRequest("item");
        indexRequest.source(ESConstant.ITEM_MAPPING, XContentType.JSON);
        restHighLevelClient.indices().create(indexRequest, RequestOptions.DEFAULT);
        return new Result(200,"创建索引库和映射成功！！",true);
    }

    @Override
    public void importData() {
        int i = 1;
        while (true) {
            BulkRequest request = new BulkRequest();
            Result result = itemClient.page(i, 1000);
            Object obj = result.getData();
            String objStr = JSON.toJSONString(obj);
            List<ItemDoc> list = JSON.parseArray(objStr, ItemDoc.class);
            if (list != null && list.size() != 0) {
                for (ItemDoc item : list) {
                    // 3.把新增文档请求对象封装到批量请求对象中
                    request.add(new IndexRequest("item") // 2.构建新增文档请求对象
                            // 一定要指定id，保证幂等性(不管调用多少次查询接口，结果都是一样的)
                            .id(String.valueOf(item.getId()))
                            .source(JSON.toJSONString(item), XContentType.JSON));
                }
                // 4.发送请求
                try {
                    BulkResponse response = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
                    i++;
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } else {
                break;
            }

        }
    }
    /**
     * 功能描述:
     * @param searchDTO
     * @return : com.heima.common.PageResult
     */
    @Override
    public PageResult search(SearchDTO searchDTO) {
        //1.校验参数
        String keyword = searchDTO.getKeyword();
        if(StringUtils.isBlank(keyword)){
            throw new RuntimeException("抱歉，没有找到与关键词相关的商品!");
        }
        //2、封装搜索请求
        SearchRequest searchRequest = packageRequest(searchDTO);
        try {
            //3.执行搜搜
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //4.解析结果
            //4.1.解析结果集

            //4.2.解析搜索面板过滤条件
            Map<String, List<String>> filterMap = filterMap(response);
            //5、封装返回数据
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;
    }
    /**
     * 功能描述: 搜索面板-过滤条件封装
     * @param response
     * @return : java.util.Map<java.lang.String,java.util.List<java.lang.String>>
     */
    public Map<String, List<String>> filterMap(SearchResponse response){
        Map<String, List<String>> filterMap = new HashMap<>();
        List<String> brandLists = new ArrayList<>();
        List<String> categoryLists = new ArrayList<>();
        Aggregations aggregations = response.getAggregations();
        if (aggregations != null) {
            Terms brandList = aggregations.get("brandList");
            Terms categoryList = aggregations.get("categoryList");
            List<? extends Terms.Bucket> brandBuckets = brandList.getBuckets();
            for (Terms.Bucket bucket : brandBuckets) {
                brandLists.add(bucket.getKey().toString());
            }
            List<? extends Terms.Bucket> categoryBuckets = categoryList.getBuckets();
            for (Terms.Bucket categoryBucket : categoryBuckets) {
                categoryLists.add(categoryBucket.getKey().toString());
            }
            filterMap.put("category", categoryLists);
            filterMap.put("brand", brandLists);
        }
        return filterMap;
    }
    /**
     * 第二步：封装搜索请求对象
     * @param searchDTO
     * @return : org.elasticsearch.action.search.SearchRequest
     */
    public SearchRequest packageRequest(SearchDTO searchDTO){
        SearchRequest searchRequest = new SearchRequest(ItemConstant.ITEM_INDEX);
        //2、DSL
        SearchSourceBuilder sourceBuilder = searchRequest.source();
        //3、函数分值
        FunctionScoreQueryBuilder functionScoreQuery = null;
        BoolQueryBuilder bool = QueryBuilders.boolQuery();
        //查询redis竞价排名数据，如果存在则，添加竞价排名过滤条件，无则不添加
        List list = redisTemplate.opsForList().range(ItemConstant.JJPM, 0, -1);
        if(list == null){
            functionScoreQuery = QueryBuilders.functionScoreQuery(bool);
        }else{
            FunctionScoreQueryBuilder.FilterFunctionBuilder[] a = {};
            List<FunctionScoreQueryBuilder.FilterFunctionBuilder> fsList = new ArrayList<>();
            for (Object o : list) {
                FunctionScoreQueryBuilder.FilterFunctionBuilder functionBuilder = new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                        QueryBuilders.termQuery("",""),
                        ScoreFunctionBuilders.weightFactorFunction(10f)
                );
                fsList.add(functionBuilder);
            }
            fsList.toArray(a);
            QueryBuilders.functionScoreQuery(bool,a);
        }
        //4、复合查询
        bool.must(QueryBuilders.matchQuery("all",searchDTO.getKeyword()));
        String brand = searchDTO.getBrand();
        if(StringUtils.isNotBlank(brand)){
            String[] split = brand.split(",");
            QueryBuilders.termsQuery("brand", Arrays.asList(split));
        }
        //。。。。。排序、价格区间
        sourceBuilder.query(functionScoreQuery);
        sourceBuilder.from(2).size(20);
        sourceBuilder.sort("price", SortOrder.DESC);
        sourceBuilder.aggregation(AggregationBuilders.terms("brandList").field("brand").size(100));
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryList").field("category").size(100));
        return searchRequest;
    }
}
